import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

# 读取图像
img = cv.imread("image/horse.jpg", 0)
plt.imshow(img, cmap=plt.cm.gray)
plt.show()

"""
API：
    Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
参数：
    src：传入的图像
    ddepth: 图像的深度
    dx和dy: 指求导的阶数，0表示这个方向上没有求导，取值为0、1。
    ksize: 是Sobel算子的大小，即卷积核的大小，必须为奇数1、3、5、7，默认为3。
    注意：如果ksize=-1，就演变成为3x3的Scharr算子。
    scale：缩放导数的比例常数，默认情况为没有伸缩系数。
    borderType：图像边界的模式，默认值为cv2.BORDER_DEFAULT。
    Sobel函数求完导数后会有负值，还有会大于255的值。而原图像是uint8，即8位无符号数，
    所以Sobel建立的图像位数不够，会有截断。因此要使用16位有符号的数据类型，即cv2.CV_16S。
    处理完图像后，再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式，否则图像无法显示。
Sobel算子是在两个方向计算的，最后还需要用cv2.addWeighted( )函数将其组合起来
    Scale_abs = cv2.convertScaleAbs(x)  # 格式转换函数
    result = cv2.addWeighted(src1, alpha, src2, beta) # 图像混合
"""

# Sobel边缘检测算法(较为简单，实际应用中效率较高，对细节纹理的检测不如canny，抗噪声能力强)
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
# 格式转换
absx = cv.convertScaleAbs(x)
absy = cv.convertScaleAbs(y)
res1 = cv.addWeighted(absx, 0.5, absy, 0.5, 0)
plt.imshow(res1, cmap=plt.cm.gray)
plt.show()

# Schaar边缘检测算法(将Sobel算法的核加以改进，速度和Sobel一样快，但结果更加精准)
x = cv.Sobel(img, cv.CV_16S, 1, 0, ksize=-1)
y = cv.Sobel(img, cv.CV_16S, 0, 1, ksize=-1)
# 格式转换
absx = cv.convertScaleAbs(x)
absy = cv.convertScaleAbs(y)
res2 = cv.addWeighted(absx, 0.5, absy, 0.5, 0)
plt.imshow(res2, cmap=plt.cm.gray)
plt.show()


# Laplacian算法(实测：Laplacian的检测效果图较暗，失去一些细节，Schaar的检测效果最亮，Sobel检测效果适中)
res3 = cv.Laplacian(img, cv.CV_16S)
# 格式转换
res3 = cv.convertScaleAbs(res3)
plt.imshow(res3, cmap=plt.cm.gray)
plt.show()


# Canny边缘检测算法(据说是最好用的,就目前我测试来看，Canny算法的图像边界不如Sobel那么清晰，但是有更多的细节)
# (虽说Canny算法的第一步时进行高斯滤波，但是在测试之前那只加了高斯噪声的狗时出现了出乎意料的事，可能库里的Canny函数没内置高斯滤波)
# (不过在Canny之前再对狗进行高斯滤波后，再测试Canny效果仍然不尽如人意，可能这只是个个例,当然高斯滤波效果确实也不咋样，所以也有可能是这个因素影响了测试结果)
# (当然检测效果不尽如人意的因素很多，但是我们还是要在编程的路上追求更完美的检测算法)
# Canny(图片，最小值，最大值)
res4 = cv.Canny(img, 0, 100)
plt.imshow(res4, cmap=plt.cm.gray)
plt.show()